package controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import pluginFactory.PluginsCharger;
import pluginInterfaces.IRecommender;

import modelInterfaces.IProject;

import utils.Messages;
import utils.output.OutputJarFile;
import controllerInterfaces.IControllerCLI;
import controllerInterfaces.IControllerMain;
import dao.DAOException;
import dao.DAOFactory;
import dao.DBConnection;


/**
 * Controller class manages command line mode RBox execution. 
 * 
 * @author Salvador Cortés
 */
public class ControllerCLI implements IControllerCLI {
	
	/**Main Controller Class**/
	private IControllerMain myController;
	
	/**Database user**/
	private String dbUser = "";
	
	/**Database user password**/
	private String dbPass = "";
	
	/**Database URI**/
	private String dbURI = "";
	
	/**RBox Project File**/
	private String projectFile = "";
	
	/**Indicate if RBox CLI will show help text. Default, false**/
	private boolean showHelp = false;
	
	/**Recommendation Component Filename. Default, "recommendation-component"**/
	private String recommComponentFile = "recommendation-component";

	/**Project Class Interface Instance**/
	private IProject project;

	/**Indicates Rbox Version **/
	private String rboxVersion;

	
	/**
	* Sets the main controller as controller of this class.
	* 
	* @param myController      main controller class.
	*/	
	public void setMainController(IControllerMain myController) {
		this.myController = myController;
		this.rboxVersion = this.myController.getRboxVersion();
	}
	
	
	/**
	 * Load and validate arguments from command line
	 * @param args loaded arguments list from command line
	 * 
	 */
	public void loadArguments(String[] args) {
	
		for(int i=0; i<args.length; i++)
    	{
    		switch (args[i].toUpperCase())
    		{
				case "-H":
					showHelp = true;
					break;	
    		
				case "-D":
					dbURI = args[++i];
					break;
				
				case "-U":
					dbUser = args[++i];
					break;
				
				case "-P":
					dbPass = args[++i];
					break;
				
				case "-F":
					projectFile = args[++i];
					break;	
					
				case "-R":
					recommComponentFile = args[++i];
					recommComponentFile.replace(".jar", "");
					break;
					
				default:
					break;
			}
    	}
    	
    	System.out.println("-----------------------------------------\n");
		
    	/*Valida argumentos*/
		String problems = "";
    	if(showHelp)
    	{
    		showHelpAndExit();
    	}
		
    	if(dbURI.isEmpty())
    	{
    		problems += Messages.errorDbURICLI+"\n";
    	}
    	
    	if(dbUser.isEmpty())
    	{
    		problems += Messages.errorDbUserCLI+"\n";
    	}
    	
    	if(dbPass.isEmpty())
    	{
    		problems += Messages.errorDbPassCLI+"\n";
    	}
    	
    	if(projectFile.isEmpty())
    	{
    		problems += Messages.errorProjectFileCLI+"\n";
    	}
    	
    	if(!problems.isEmpty())
    	{
    		problems += "\n" + Messages.exitCLI + "\n";
    		System.out.println(problems);
    		showHelpAndExit();
    	}
    	/*Fin valida argumentos*/
    	
    	
    	/*Testea base de datos*/
    	if(!isDBConnected())
    	{
    		exit();
    	}
    	
    	/*Carga archivo componente de recomendación JAR*/
    	if(!isProjectFileLoaded())
    	{
    		exit();
    	}
	}

	
	/**
     * Closes the application
     */
	private void exit() {
		System.out.println(Messages.exitCLI);
		System.out.println("-----------------------------------------");
		this.myController.normalExit();
	}


	/**
	 * Shows help text and terminates the application
	 */
	private void showHelpAndExit() {
		String help = "";
		help += rboxVersion  + " Help\n";
		help += "Use: java -jar RBox.jar --mode=[gui|cli] [arguments]\n";
		help += "\n";
		help += "RBox is a tool to make recommendation algorithms experimentation and evaluation metrics, and generate components of recommendation.\n";
		help += "\n";
		help += "Arguments:\n";
		help += "-h\tThis help text. Optional.\n";
		help += "-d\tDatabase URI. Required.\n";
		help += "-u\tDatabase user. Required.\n";
		help += "-p\tDatabase user password. Required.\n";
		help += "-f\tRBox Project File. Required.\n";
		help += "-r\tFile to save recommendation component. Optional, default: "+recommComponentFile+".\n";
		System.out.println(help);
		System.out.println("-----------------------------------------");
		this.myController.abnormalExit();
	}

	/**
	 * 
	 * @return true if project file is loaded successful
	 */
	private boolean isProjectFileLoaded() {
		try
		{
			File pf = new File(projectFile);
			ObjectInputStream oi = new ObjectInputStream(new BufferedInputStream(new FileInputStream(pf)));
			Object obj = oi.readObject();
			oi.close();
			this.project = (IProject) obj;
			System.out.println(Messages.successfullLoadProjectFile);
			return true;
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage() + " - " + e.getCause());
		}
		return false;
	}


	private boolean isDBConnected() {
		DBConnection.setURI(dbURI);
		DBConnection.setUser(dbUser);
		DBConnection.setPassword(dbPass);
		
		try
		{
			if (DAOFactory.getDAOFactory().getTestConnection().isEstablished())
			{
				System.out.println(Messages.successfullDBConnection);
				return true;
			}
		}
		catch (DAOException e)
		{
			System.out.println(e.getMessage() + " - " + e.getCause());
		}
		return false;
	}

	
	/**
	 * Execute global operations for init CLI Execution mode RBOX 
	 */
	public void execute() {
		if (createOutputJAR(recommComponentFile, ".", this.project.getRecommenderList().get(0)))
		{
			System.out.println(Messages.successfulJARCreation);
			System.out.println("-----------------------------------------");
			this.myController.normalExit();
		}
		else
		{
			System.out.println(Messages.errorJARCreation);
			System.out.println("-----------------------------------------");
			this.myController.abnormalExit();
		}
	}
	
	
	/**
	* Creates the output component as a jar file.
	* 
 	* @param nameFile      jar file name.
 	* @param dirPath      jar file directory.
 	* @param recom      configuration algorithm that is included in the jar file.
 	* @return true if creation of JAR component is successful, otherwise, false.
	*/
	private boolean createOutputJAR(String nameFile, String dirPath, IRecommender recom) {
		String directoryAlgorithm = getDirectoryAlgorithm(recom);
		File dir = new File("Temp//config");
		if ( !dir.exists() )  
            dir.mkdirs(); 
		File alg = new File("Temp//config//AlgorithmConfig");
		try {
				ObjectOutputStream oo = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(alg)));
				oo.writeObject(recom.getAlgorithm());
				oo.close();
				new OutputJarFile().generateJarOutput(nameFile, dirPath, directoryAlgorithm);
				return true;
			} catch (FileNotFoundException e1) {
				System.out.println(Messages.errorCreateJARCLI);
				System.out.println(e1.getMessage());
			} catch (IOException e1) {
				System.out.println(Messages.errorCreateJARCLI);
				System.out.println(e1.getMessage());
			}	
		return false;
	}
	
	/**
	* Returns the directory of "algorithm" that is related to the configuration algorithm that comes as a parameter. 
	* Deliver the plugin path recommendation algorithmCreates the output component as a jar file.
	* 
 	* @param recom      configuration algorithm.
 	* @return algorithm PATH of plugin.
	*/
	private String getDirectoryAlgorithm(IRecommender recom){
		String algotihmName = recom.getAlgorithm().getClass().getCanonicalName();
		File[] jars = PluginsCharger.Finder("plugins//recommender");
		algotihmName = (algotihmName.replace(".", "/") +".class");
    	try {
    		for(File jar : jars){
				JarInputStream jarInput = new JarInputStream(new FileInputStream(jar));
				JarEntry jarEntry=null;
		    	
		    	while((jarEntry=jarInput.getNextJarEntry())!=null){
		    		String entryName = jarEntry.getName();
		    		
		    		if(algotihmName.equals(entryName))
		    		{
		    			return jar.getPath();
		    		}
		    	}
    		}
		} catch (IOException e1) {
			System.out.println(Messages.errorFindAlgorithmPluginCLI);
			System.out.println(e1.getMessage());
		}
		return null;
	}
	

}
